package com.example.hxds.odr.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.example.hxds.common.exception.HxdsException;
import com.example.hxds.common.util.ConstantPropertiesUtils;
import com.example.hxds.common.util.HttpClient;
import com.example.hxds.common.util.PageUtils;
import com.example.hxds.common.wxpay.WXPayUtil;
import com.example.hxds.odr.controller.form.TransferForm;
import com.example.hxds.odr.db.dao.OrderBillDao;
import com.example.hxds.odr.db.dao.OrderCommentDao;
import com.example.hxds.odr.db.dao.OrderDao;
import com.example.hxds.odr.db.dao.OrderProfitsharingDao;
import com.example.hxds.odr.db.pojo.OrderBillEntity;
import com.example.hxds.odr.db.pojo.OrderEntity;
import com.example.hxds.odr.fegin.DrServiceApi;
import com.example.hxds.odr.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderDao orderDao;
    @Resource
    private OrderCommentDao orderCommentDao;
    @Resource
    private OrderProfitsharingDao orderProfitsharingDao;
    @Resource
    private OrderBillDao orderBillDao;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DrServiceApi drServiceApi;

    @Override
    public HashMap searchDriverTodayBusinessData(long driverId) {
        HashMap result = orderDao.searchDriverTodayBusinessData(driverId);
        return result;
    }

    @Override
    public HashMap searchDriverMonthByPositiveAndNegativeReviews(long driverId) {
        return orderCommentDao.searchDriverMonthByPositiveAndNegativeReviews(driverId);
    }

    @Override
    public HashMap searchDriverYearBusinessMonth(long driverId) {
        return orderDao.searchDriverYearBusinessMonth(driverId);
    }

    @Override
    public List<HashMap> searchDriverYearByMonthlyOrderSplittingPersonalIncomeTax(long driverId) {
        List<HashMap> hashMaps = orderProfitsharingDao.searchDriverYearByMonthlyOrderSplittingPersonalIncomeTax(driverId);
        return hashMaps;
    }

    @Override
    @Transactional
    @LcnTransaction
    public String insertOrder(OrderEntity orderEntity, OrderBillEntity billEntity) {
        //插入记录
        int insert = orderDao.insert(orderEntity);
        if (insert == 1) {
            String orderId = orderDao.searchOrderIdByUUID(orderEntity.getUuid());
            //插入订单费用记录
            billEntity.setOrderId(Long.parseLong(orderId));
            int row = orderBillDao.insert(billEntity);
            if (row == 1) {
                //往Redis里面插入缓存,配合Redis事务用于司机抢单,避免多个司机同时抢单成功
                redisTemplate
                        .opsForValue()
                        .set("order#" + orderId, "none", 16, TimeUnit.MINUTES); //缓存16分钟
                return orderId;
            } else {
                throw new HxdsException("保存新订单费用失败");
            }
        } else {
            throw new HxdsException("保存新订单失败");
        }
    }

    @Override
    @Transactional
    @LcnTransaction
    public String acceptNewOrder(long driverId, long orderId) {
        //Redis不存在抢单的新订单就代表抢单失败
        if (!redisTemplate.hasKey("order#" + orderId)) {
            return "抢单失败";
        }
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations redisOperations) throws DataAccessException {
                //获取新订单记录的Version
                redisOperations.watch("order#" + orderId);
                //本地缓存
                redisOperations.multi();
                //把新订单缓存的Value设置成抢单司机的ID
                redisOperations.opsForValue().set("order#" + orderId, driverId);
                //执行redis事务,如果事务执行失败会自动抛出异常
                return redisOperations.exec();
            }
        });
        //抢单成功之后,删除Redis中的新订单,避免让其他司机参与抢单
        redisTemplate.delete("order#" + orderId);

        //更新订单记录,添加上接单司机ID和接单时间
        HashMap param = new HashMap() {{
            put("driverId", driverId);
            put("orderId", orderId);
        }};
        int row = orderDao.acceptNewOrder(param);
        if (row != 1) {
            throw new HxdsException("接单失败,无法更新订单记录");
        }
        return "接单成功";
    }

    @Override
    public HashMap searchDriverExecuteOrder(Map param) {
        HashMap map = orderDao.searchDriverExecuteOrder(param);
        return map;
    }

    @Override
    public Integer searchOrderStatus(Map param) {
        Integer status = orderDao.searchOrderStatus(param);
        if (status == null) {
            status = 0;
        }
        return status;
    }


    @Override
    @Transactional
    @LcnTransaction
    public String deleteUnAcceptOrder(Map param) {
        long orderId = MapUtil.getLong(param, "orderId");
        String key = "order#" + orderId;
        if (!redisTemplate.hasKey(key)) {
            return "订单取消失败";
        }
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations redisOperations) throws DataAccessException {
                redisOperations.watch(key);
                redisOperations.multi();
                redisOperations.opsForValue().set(key, "none");
                return redisOperations.exec();
            }
        });

        redisTemplate.delete(key);
        int i = orderDao.deleteUnAcceptOrder(param);
        if (i != 1) {
            return "订单取消失败";
        }
        int rows = orderBillDao.deleteUnAcceptOrderBill(orderId);
        if (rows == 1) {
            return "订单取消失败";
        }
        return "订单取消成功";

    }

    @Override
    public HashMap searchDriverCurrentOrder(long driverId) {
        HashMap map = orderDao.searchDriverCurrentOrder(driverId);
        return map;
    }

    @Override
    public HashMap hasCustomerCurrentOrder(long customerId) {
        HashMap result = new HashMap();

        //根据客户Id查询订单信息（没有司机接单的订单）
        HashMap map = orderDao.hasCustomerUnAcceptOrder(customerId);
        result.put("hasCustomerUnAcceptOrder", map != null);
        result.put("unAcceptOrder", map);

        //根据客户Id查询订单ID（有司机接单的订单）
        Long orderId = orderDao.hasCustomerUnFinishedOrder(customerId);
        result.put("hasCustomerUnFinishedOrder", orderId != null);
        result.put("unFinishedOrder", orderId);

        return result;
    }

    @Override
    public HashMap searchOrderForMoveById(Map param) {
        HashMap map = orderDao.searchOrderForMoveById(param);
        return map;
    }

    @Override
    public int arriveStartPlace(Map param) {
        String orderId = MapUtil.getStr(param, "orderId");
        String key = "order_driver_arrivied#" + orderId;
        redisTemplate.opsForValue().set(key, "1");
        int row = orderDao.updateOrderStatus(param);
        if (row != 1) {
            throw new HxdsException("更新订单状态失败");
        }
        return row;
    }

    //乘客确认实际已经到达
    @Override
    public boolean confirArriveStartPlace(long orderId) {
        String key = "order_driver_arrivied#" + orderId;
        if (redisTemplate.hasKey(key) && redisTemplate.opsForValue().get(key).toString().equals("1")) {
            redisTemplate.opsForValue().set(key, "2");
            return true;
        }
        return false;
    }

    //司机点击开始代驾
    @Override
    @Transactional
    @LcnTransaction
    public int startDriving(Map param) {
        String orderId = MapUtil.getStr(param, "orderId");
        String key = "order_driver_arrivied#" + orderId;
        if (redisTemplate.hasKey(key) && redisTemplate.opsForValue().get(key).toString().equals("2")) {
            redisTemplate.delete(key);
            int rows = orderDao.updateOrderStatus(param);
            if (rows != 1) {
                throw new HxdsException("更新订单状态失败");
            }
            return rows;
        }
        return 0;
    }

    @Override
    @Transactional
    @LcnTransaction
    public int updateOrderStatus(Map param) {
        int rows = orderDao.updateOrderStatus(param);
        if (rows != 1) {
            throw new HxdsException("更新取消订单记录失败");
        }
        return rows;
    }


    @Override
    public PageUtils searchOrderByPage(Map param) {
        long count = orderDao.searchOrderCount(param);
        ArrayList<HashMap> list = null;
        if (count == 0) {
            list = new ArrayList();
        } else {
            list = orderDao.searchOrderByPage(param);
        }
        int start = (Integer) param.get("start");
        int length = (Integer) param.get("length");
        PageUtils pageUtils = new PageUtils(list, count, start, length);
        return pageUtils;
    }

    @Override
    public HashMap searchOrderContent(long orderId) {
        HashMap map = orderDao.searchOrderContent(orderId);
        JSONObject startPlaceLocation = JSONUtil.parseObj(MapUtil.getStr(map, "startPlaceLocation"));
        JSONObject endPlaceLocation = JSONUtil.parseObj(MapUtil.getStr(map, "endPlaceLocation"));
        map.replace("startPlaceLocation", startPlaceLocation);
        map.replace("endPlaceLocation", endPlaceLocation);
        return map;
    }

    //查询最近30天代驾上车点的坐标
    @Override
    public ArrayList<HashMap> searchOrderStartLocationIn30Days() {
        ArrayList<String> list = orderDao.searchOrderStartLocationIn30Days();
        ArrayList<HashMap> resutl = new ArrayList<>();
        list.forEach(location -> {
            JSONObject json = JSONUtil.parseObj(location);
            String latitude = json.getStr("latitude");
            String longitude = json.getStr("longitude");
//            latitude = latitude.substring(0, latitude.length() - 4);
//            latitude += "0001";
//            longitude = longitude.substring(0, longitude.length() - 4);
//            longitude += "0001";
            HashMap map = new HashMap();
            map.put("latitude", latitude);
            map.put("longitude", longitude);
            resutl.add(map);
        });
        return resutl;
    }

    @Override
    public boolean validDriverOwnOrder(Map param) {
        long count = orderDao.validDriverOwnOrder(param);
        return count == 1 ? true : false;
    }

    @Override
    public HashMap searchSettlementNeedData(long orderId) {
        HashMap map = orderDao.searchSettlementNeedData(orderId);
        return map;
    }

    //根据订单Id 查询订单信息
    @Override
    public HashMap searchOrderById(Map param) {
        HashMap map = orderDao.searchOrderById(param);
        String startPlaceLocation = MapUtil.getStr(map, "startPlaceLocation");
        String endPlaceLocation = MapUtil.getStr(map, "endPlaceLocation");
        map.replace("startPlaceLocation", JSONUtil.parseObj(startPlaceLocation));
        map.replace("endPlaceLocation", JSONUtil.parseObj(endPlaceLocation));
        return map;
    }


    @Override
    //查询订单是否可以进行支付
    public HashMap validCanPayOrder(Map param) {
        HashMap map = orderDao.validCanPayOrder(param);
        if (map == null || map.size() == 0) {
            throw new HxdsException("订单无法支付");
        }
        return map;
    }

    @Override
    @Transactional
    @LcnTransaction
    public int updateOrderPrepayId(Map param) {
        int rows = orderDao.updateOrderPrepayId(param);
        if (rows != 1) {
            throw new HxdsException("更新预支付订单ID失败");
        }
        return rows;
    }

    @Override
    public Map<String, String> queryPayStatus(Long orderId) {
        try {
            // 1.封装提交参数
            HashMap map = orderDao.searchUuidAndStatus(orderId);
            String outTradeNo = MapUtil.getStr(map, "uuid");
            Map paramMap = new HashMap<>();
            paramMap.put("appid", ConstantPropertiesUtils.APPID);
            paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER);
            paramMap.put("out_trade_no", outTradeNo);
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
            //2.设置请求内容
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
            client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, ConstantPropertiesUtils.PARTNERKEY));
            client.setHttps(true);
            client.post();
            //4.得到微信接口返回数据
            String xml = client.getContent();
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
            System.out.println("支付状态resultMap****:" + resultMap);
            //5.把接口数据返回
            return resultMap;
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    @Transactional
    @LcnTransaction
    public void handlePayment(String uuid, String payId, String driverOpenId, String payTime) {

        //先查询 订单状态
        HashMap map = orderDao.searchOrderIdAndStatus(uuid);
        Integer status = MapUtil.getInt(map, "status");
        if (status == 7 || status == 8) {
            return;
        }
        HashMap param = new HashMap() {{
            put("uuid", uuid);
            put("payId", payId);
            put("payTime", payTime);
        }};
        //更新订单记录的payId，状态和付款时间
        int rows = orderDao.updateOrderPayIdAndStatus(param);
        if (rows != 1) {
            throw new HxdsException("更新支付订单记录失败");
        }

        orderDao.searchDriverIdAndIncentiveFee(uuid);
        rows = orderDao.finishOrder(uuid);
        if (rows != 1) {
            throw new HxdsException("更新订单结束状态失败");
        }
    }

    @Override
    @Transactional
    @LcnTransaction
    public String updateOrderAboutPayment(Map param) {
        Long orderId = MapUtil.getLong(param, "orderId");
        HashMap map = orderDao.searchUuidAndStatus(orderId);
        String uuid = MapUtil.getStr(map, "uuid");
        Integer status = MapUtil.getInt(map, "status");
        //如果订单状态已经是已付款，就退出当前方法
        if (status == 7 || status == 8) {
            return "付款成功";
        }
        try {
            //查询支付参数结果
            Map paramMap = new HashMap<>();
            paramMap.put("appid", ConstantPropertiesUtils.APPID);
            paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER);
            paramMap.put("out_trade_no", uuid);
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
            //2.设置请求内容
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
            client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, ConstantPropertiesUtils.PARTNERKEY));
            client.setHttps(true);
            client.post();
            //4.得到微信接口返回数据
            String xml = client.getContent();
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
            System.out.println("支付状态resultMap****:" + resultMap);

            if ("SUCCESS".equals(resultMap.get("trade_state"))) { //支付成功，
                //更改订单状态，处理支付结果
                String driverOpenId = resultMap.get("attach");
                String outTradeNo = resultMap.get("out_trade_no");
                String transactionId = resultMap.get("transaction_id");
                String payTime = DateUtil.parse(resultMap.get("time_end"), "yyyyMMddHHmmss").toString();
                // 订单更新为已付款
                this.handlePayment(outTradeNo, transactionId, driverOpenId, payTime);
                return "付款成功";
            } else {
                return "付款异常";
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new HxdsException("更新订单相关付款信息失败");
        }
    }

    @Override
    public PageUtils searchDriverOrderByPage(Map param) {
        //查询司机订单数量
        long count = orderDao.searchDriverOrderCount(param);
        ArrayList<HashMap> result = null;
        if (count > 0) {
            result = orderDao.searchDriverOrderByPage(param);
        } else {
            result = new ArrayList<>();
        }
        int start = (Integer) param.get("start");
        int length = (Integer) param.get("length");
        PageUtils pageUtils = new PageUtils(result, count, start, length);
        return pageUtils;
    }

    @Override
    public PageUtils searchCustomerOrderByPage(Map param) {
        long count = orderDao.searchCustomerOrderCount(param);
        ArrayList<HashMap> list = null;
        if (count > 0) {
            list = orderDao.searchCustomerOrderByPage(param);
        } else {
            list = new ArrayList<>();
        }
        int start = MapUtil.getInt(param, "start");
        int length = MapUtil.getInt(param, "length");
        PageUtils pageUtils = new PageUtils(list, count, start, length);
        return pageUtils;
    }
}